/*
 * Copyright (c) 2021-2023 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "transaction/rs_transaction_data.h"

#include "command/rs_canvas_node_command.h"
#include "command/rs_command.h"
#include "command/rs_command_factory.h"
#include "platform/common/rs_log.h"
#include "platform/common/rs_system_properties.h"
#include "rs_profiler.h"

namespace Keels {
namespace Rosen {
namespace {
static constexpr size_t PARCEL_MAX_CPACITY = 4000 * 1024;     // upper bound of parcel capacity
static constexpr size_t PARCEL_SPLIT_THRESHOLD = 1800 * 1024; // should be < PARCEL_MAX_CPACITY
} // namespace

std::function<void(uint64_t, int, int)> RSTransactionData::alarmLogFunc = [](uint64_t nodeId, int count, int num) {
    ROSEN_LOGW("rsNode:%{public}" PRId64 " send %{public}d commands, "
               "total num of rsNode is %{public}d",
        nodeId, count, num);
};

RSTransactionData* RSTransactionData::Unmarshalling(Parcel& parcel)
{
    auto transactionData = new RSTransactionData();
    if (transactionData->UnmarshallingCommand(parcel)) {
        return transactionData;
    }
    ROSEN_LOGE("RSTransactionData Unmarshalling Failed");
    delete transactionData;
    return nullptr;
}

void RSTransactionData::AddAlarmLog(std::function<void(uint64_t, int, int)> func)
{
    alarmLogFunc = func;
}

RSTransactionData::~RSTransactionData()
{
    Clear();
}

void RSTransactionData::AlarmRsNodeLog() const
{
    std::unordered_map<NodeId, int> commandNodeIdCount;
    for (size_t countIndex = 0; countIndex < payload_.size(); countIndex++) {
        auto nodeId = std::get<0>(payload_[countIndex]);

        if (commandNodeIdCount.count(nodeId)) {
            commandNodeIdCount[nodeId] += 1;
        } else {
            commandNodeIdCount[nodeId] = 1;
        }
    }

    int maxCount = 0;
    NodeId maxNodeId = -1;
    int rsNodeNum = 0;

    for (auto it = commandNodeIdCount.begin(); it != commandNodeIdCount.end(); ++it) {
        if (it->second > maxCount) {
            maxNodeId = it->first;
            maxCount = it->second;
        }
        rsNodeNum++;
    }

    RSTransactionData::alarmLogFunc(maxNodeId, maxCount, rsNodeNum);
}

bool RSTransactionData::Marshalling(Parcel& parcel) const
{
    parcel.SetMaxCapacity(PARCEL_MAX_CPACITY);
    // to correct actual marshaled command size later, record its position in parcel
    size_t recordPosition = parcel.GetWritePosition();
    std::unique_lock<std::mutex> lock(commandMutex_);
    bool success = parcel.WriteInt32(static_cast<int32_t>(payload_.size()));
    size_t marshaledSize = 0;
    static bool isUniRender = RSSystemProperties::GetUniRenderEnabled();
    success = success && parcel.WriteBool(isUniRender);
    while (marshallingIndex_ < payload_.size()) {
        auto& [nodeId, followType, command] = payload_[marshallingIndex_];

        if (!isUniRender) {
            success = success && parcel.WriteUint64(nodeId);
            success = success && parcel.WriteUint8(static_cast<uint8_t>(followType));
        }
        if (!command) {
            parcel.WriteUint8(0);
            RS_LOGW("failed RSTransactionData::Marshalling, command is nullptr");
        } else if (command->indexVerifier_ != marshallingIndex_) {
            parcel.WriteUint8(0);
            RS_LOGW("failed RSTransactionData::Marshalling, indexVerifier is wrong, SIGSEGV may have occurred");
        } else {
            parcel.WriteUint8(1);
            success = success && command->Marshalling(parcel);
        }
        if (!success) {
            ROSEN_LOGE("failed RSTransactionData::Marshalling type:%{public}s", command->PrintType().c_str());
            return false;
        }
        ++marshallingIndex_;
        ++marshaledSize;
        if ((RSSystemProperties::GetUnmarshParallelFlag() &&
                parcel.GetDataSize() > RSSystemProperties::GetUnMarshParallelSize()) ||
            parcel.GetDataSize() > PARCEL_SPLIT_THRESHOLD) {
            break;
        }
    }
    if (marshaledSize < payload_.size()) {
        // correct command size recorded in Parcel
        *reinterpret_cast<int32_t*>(parcel.GetData() + recordPosition) = static_cast<int32_t>(marshaledSize);
        ROSEN_LOGW("RSTransactionData::Marshalling data split to several parcels"
                   ", marshaledSize:%{public}zu, marshallingIndex_:%{public}zu, total count:%{public}zu"
                   ", parcel size:%{public}zu, threshold:%{public}zu.",
            marshaledSize, marshallingIndex_, payload_.size(), parcel.GetDataSize(), PARCEL_SPLIT_THRESHOLD);

        AlarmRsNodeLog();
    }
    success = success && parcel.WriteBool(needSync_);
    success = success && parcel.WriteBool(needCloseSync_);
    success = success && parcel.WriteInt32(syncTransactionCount_);
    success = success && parcel.WriteUint64(timestamp_);
    success = success && parcel.WriteInt32(pid_);
    success = success && parcel.WriteUint64(index_);
    success = success && parcel.WriteUint64(syncId_);
    success = success && parcel.WriteInt32(parentPid_);
    return success;
}

void RSTransactionData::ProcessBySingleFrameComposer(RSContext& context)
{
    std::unique_lock<std::mutex> lock(commandMutex_);
    for (auto& [nodeId, followType, command] : payload_) {
        if (command != nullptr && command->GetType() == RSCommandType::CANVAS_NODE &&
            command->GetSubType() == RSCanvasNodeCommandType::CANVAS_NODE_UPDATE_RECORDING) {
            command->Process(context);
        }
    }
}

void RSTransactionData::Process(RSContext& context)
{
    std::unique_lock<std::mutex> lock(commandMutex_);
    for (auto& [nodeId, followType, command] : payload_) {
        if (command != nullptr) {
            if (!command->IsCallingPidValid()) {
                continue;
            }
            command->Process(context);
        }
    }
}

void RSTransactionData::Clear()
{
    std::unique_lock<std::mutex> lock(commandMutex_);
    payload_.clear();
    payload_.shrink_to_fit();
    timestamp_ = 0;
}

void RSTransactionData::AddCommand(std::unique_ptr<RSCommand>& command, NodeId nodeId, FollowType followType)
{
    std::unique_lock<std::mutex> lock(commandMutex_);
    if (command) {
        command->indexVerifier_ = payload_.size();
        payload_.emplace_back(nodeId, followType, std::move(command));
    }
}

void RSTransactionData::AddCommand(std::unique_ptr<RSCommand>&& command, NodeId nodeId, FollowType followType)
{
    std::unique_lock<std::mutex> lock(commandMutex_);
    if (command) {
        command->indexVerifier_ = payload_.size();
        payload_.emplace_back(nodeId, followType, std::move(command));
    }
}

bool RSTransactionData::UnmarshallingCommand(Parcel& parcel)
{
    Clear();

    int commandSize = 0;
    if (!parcel.ReadInt32(commandSize)) {
        ROSEN_LOGE("RSTransactionData::UnmarshallingCommand cannot read commandSize");
        return false;
    }
    uint8_t followType = 0;
    NodeId nodeId = 0;
    uint8_t hasCommand = 0;
    uint16_t commandType = 0;
    uint16_t commandSubType = 0;

    size_t readableSize = parcel.GetReadableBytes();
    size_t len = static_cast<size_t>(commandSize);
    if (len > readableSize || len > payload_.max_size()) {
        ROSEN_LOGE("RSTransactionData UnmarshallingCommand Failed read vector, size:%{public}zu,"
                   " readAbleSize:%{public}zu",
            len, readableSize);
        return false;
    }

    bool isUniRender = false;
    if (!parcel.ReadBool(isUniRender)) {
        ROSEN_LOGE("RSTransactionData::UnmarshallingCommand cannot read isUniRender");
        return false;
    }
    std::unique_lock<std::mutex> payloadLock(commandMutex_, std::defer_lock);
    for (size_t i = 0; i < len; i++) {
        if (!isUniRender) {
            if (!parcel.ReadUint64(nodeId)) {
                ROSEN_LOGE("RSTransactionData::UnmarshallingCommand cannot read nodeId");
                return false;
            }
            if (!parcel.ReadUint8(followType)) {
                ROSEN_LOGE("RSTransactionData::UnmarshallingCommand cannot read followType");
                return false;
            }
        }
        if (!parcel.ReadUint8(hasCommand)) {
            ROSEN_LOGE("RSTransactionData::UnmarshallingCommand cannot read hasCommand");
            return false;
        }
        if (hasCommand) {
            if (!(parcel.ReadUint16(commandType) && parcel.ReadUint16(commandSubType))) {
                return false;
            }
            auto func = RSCommandFactory::Instance().GetUnmarshallingFunc(commandType, commandSubType);
            if (func == nullptr) {
                return false;
            }
            auto command = (*func)(parcel);
            if (command == nullptr) {
                ROSEN_LOGE("failed RSTransactionData::UnmarshallingCommand, type=%{public}d subtype=%{public}d",
                    commandType, commandSubType);
                return false;
            }
            RS_PROFILER_PATCH_COMMAND(parcel, command);
            payloadLock.lock();
            payload_.emplace_back(nodeId, static_cast<FollowType>(followType), std::move(command));
            payloadLock.unlock();
        } else {
            continue;
        }
    }
    int32_t pid;
    return parcel.ReadBool(needSync_) && parcel.ReadBool(needCloseSync_) && parcel.ReadInt32(syncTransactionCount_) &&
           parcel.ReadUint64(timestamp_) && ({
               RS_PROFILER_PATCH_TRANSACTION_TIME(parcel, timestamp_);
               true;
           }) &&
           parcel.ReadInt32(pid) && ({
               RS_PROFILER_PATCH_PID(parcel, pid);
               pid_ = pid;
               true;
           }) &&
           parcel.ReadUint64(index_) && parcel.ReadUint64(syncId_) && parcel.ReadInt32(parentPid_);
}

bool RSTransactionData::IsCallingPidValid(
    pid_t callingPid, const RSRenderNodeMap& nodeMap, pid_t& conflictCommandPid, std::string& commandMapDesc) const
{
    // Since GetCallingPid interface always returns 0 in asynchronous binder in Linux kernel system,
    // we temporarily add a white list to avoid abnormal functionality or abnormal display.
    // The white list will be removed after GetCallingPid interface can return real PID.
    if (callingPid == 0) {
        return true;
    }

    std::unordered_map<pid_t, std::unordered_map<NodeId, std::set<std::pair<uint16_t, uint16_t>>>>
        conflictPidToCommandMap_;
    std::unique_lock<std::mutex> lock(commandMutex_);
    for (auto& [_, followType, command] : payload_) {
        if (command == nullptr) {
            continue;
        }
        const NodeId nodeId = command->GetNodeId();
        const pid_t commandPid = ExtractPid(nodeId);
        if (callingPid == commandPid) {
            continue;
        }
        if (nodeMap.IsUIExtensionSurfaceNode(nodeId)) {
            continue;
        }
        conflictPidToCommandMap_[commandPid][nodeId].insert(command->GetUniqueType());
        command->SetCallingPidValid(false);
    }
    lock.unlock();
    for (const auto& [commandPid, commandTypeMap] : conflictPidToCommandMap_) {
        conflictCommandPid = commandPid;
        commandMapDesc = PrintCommandMapDesc(commandTypeMap);
        return false;
    }
    return true;
}

std::string RSTransactionData::PrintCommandMapDesc(
    const std::unordered_map<NodeId, std::set<std::pair<uint16_t, uint16_t>>>& commandTypeMap) const
{
    std::string commandMapDesc = "{ ";
    for (const auto& [nodeId, commandTypeSet] : commandTypeMap) {
        std::string commandSetDesc = std::to_string(nodeId) + ": [";
        for (const auto& [commandType, commandSubType] : commandTypeSet) {
            std::string commandDesc = "(" + std::to_string(commandType) + "," + std::to_string(commandSubType) + "),";
            commandSetDesc += commandDesc;
        }
        commandSetDesc += "], ";
        commandMapDesc += commandSetDesc;
    }
    commandMapDesc += "}";
    return commandMapDesc;
}

class RSNode;
const int32_t GetFrameNodeId(NodeId id);
const std::string GetFrameNodeTag(NodeId id);

const char* RSCommandTypeToString(uint16_t type) {
    switch (type) {
        case BASE_NODE: return "BASE_NODE";
        case RS_NODE: return "RS_NODE";
        case CANVAS_NODE: return "CANVAS_NODE";
        case SURFACE_NODE: return "SURFACE_NODE";
        case PROXY_NODE: return "PROXY_NODE";
        case ROOT_NODE: return "ROOT_NODE";
        case DISPLAY_NODE: return "DISPLAY_NODE";
        case EFFECT_NODE: return "EFFECT_NODE";
        case CANVAS_DRAWING_NODE: return "CANVAS_DRAWING_NODE";
        case ANIMATION: return "ANIMATION";
        case RS_NODE_SYNCHRONOUS_READ_PROPERTY: return "RS_NODE_SYNCHRONOUS_READ_PROPERTY";
        case RS_NODE_SYNCHRONOUS_GET_VALUE_FRACTION: return "RS_NODE_SYNCHRONOUS_GET_VALUE_FRACTION";
        case FRAME_RATE_LINKER: return "FRAME_RATE_LINKER";
        default: return "UnknownCommandType";
    }
}

static const char* RSNodeCommandTypeToString(uint16_t type) {
    switch (type) {
        case 0: return "ADD_MODIFIER";
        case 1: return "REMOVE_MODIFIER";
        case 2: return "UPDATE_MODIFIER_BOOL";
        case 3: return "UPDATE_MODIFIER_FLOAT";
        case 4: return "UPDATE_MODIFIER_INT";
        case 5: return "UPDATE_MODIFIER_COLOR";
        case 6: return "UPDATE_MODIFIER_GRAVITY";
        case 7: return "UPDATE_MODIFIER_MATRIX3F";
        case 8: return "UPDATE_MODIFIER_QUATERNION";
        case 9: return "UPDATE_MODIFIER_FILTER_PTR";
        case 10: return "UPDATE_MODIFIER_IMAGE_PTR";
        case 11: return "UPDATE_MODIFIER_MASK_PTR";
        case 12: return "UPDATE_MODIFIER_PATH_PTR";
        case 13: return "UPDATE_MODIFIER_DYNAMIC_BRIGHTNESS";
        case 14: return "UPDATE_MODIFIER_GRADIENT_BLUR_PTR";
        case 15: return "UPDATE_MODIFIER_EMITTER_UPDATER_PTR";
        case 16: return "UPDATE_MODIFIER_NOISE_FIELD_PTR";
        case 17: return "UPDATE_MODIFIER_SHADER_PTR";
        case 18: return "UPDATE_MODIFIER_VECTOR2F";
        case 19: return "UPDATE_MODIFIER_VECTOR4_BORDER_STYLE";
        case 20: return "UPDATE_MODIFIER_VECTOR4_COLOR";
        case 21: return "UPDATE_MODIFIER_VECTOR4F";
        case 22: return "UPDATE_MODIFIER_RRECT";
        case 23: return "UPDATE_MODIFIER_DRAW_CMD_LIST";
        case 24: return "UPDATE_MODIFIER_DRAWING_MATRIX";
        case 25: return "SET_FREEZE";
        case 26: return "SET_DRAW_REGION";
        case 27: return "SET_OUT_OF_PARENT";
        case 28: return "SET_TAKE_SURFACE_CAPTURE_FOR_UI_FLAG";
        case 29: return "REGISTER_GEOMETRY_TRANSITION";
        case 30: return "UNREGISTER_GEOMETRY_TRANSITION";
        case 31: return "MARK_NODE_GROUP";
        case 32: return "MARK_NODE_SINGLE_FRAME_COMPOSER";
        case 33: return "MARK_SUGGEST_OPINC_NODE";
        case 34: return "MARK_UIFIRST_NODE";
        case 35: return "SET_NODE_NAME";
        case 36: return "UPDATE_MODIFIER_MOTION_BLUR_PTR";
        case 37: return "UPDATE_MODIFIER_MAGNIFIER_PTR";
        case 38: return "UPDATE_MODIFIER_WATER_RIPPLE";
        case 39: return "UPDATE_MODIFIER_FLY_OUT";
        case 40: return "REMOVE_ALL_MODIFIERS";
        default: return "UnknownRSNodeCommandType";
    }
}

static const char* RSCanvasNodeCommandTypeToString(uint16_t type) {
    switch (type) {
        case 0: return "CANVAS_NODE_CREATE";
        case 1: return "CANVAS_NODE_UPDATE_RECORDING";
        case 2: return "CANVAS_NODE_CLEAR_RECORDING";
        default: return "UnknownSubCommandType";
    }
}

static const char* RSRootNodeCommandTypeToString(uint16_t type) {
    switch (type) {
        case 0: return "ROOT_NODE_CREATE";
        case 1: return "ROOT_NODE_ATTACH";
        case 2: return "ATTACH_TO_UNI_SURFACENODE";
        case 3: return "SET_ENABLE_RENDER";
        case 4: return "UPDATE_SUGGESTED_BUFFER_SIZE";
        default: return "UnknownRSRootNodeCommandType";
    }
}

static const char* RSBaseNodeCommandTypeToString(uint16_t type) {
    switch (type) {
        case 0: return "BASE_NODE_DESTROY";
        case 1: return "BASE_NODE_ADD_CHILD";
        case 2: return "BASE_NODE_MOVE_CHILD";
        case 3: return "BASE_NODE_ADD_CROSS_PARENT_CHILD";
        case 4: return "BASE_NODE_REMOVE_CHILD";
        case 5: return "BASE_NODE_REMOVE_CROSS_PARENT_CHILD";
        case 6: return "BASE_NODE_REMOVE_FROM_TREE";
        case 7: return "BASE_NODE_CLEAR_CHILDREN";
        default: return "UnknownRSBaseNodeCommandType";
    }
}

std::string RSCommandSubTypeToString(uint16_t type, uint16_t subType) {
    switch (type) {
        case 0:
            return RSBaseNodeCommandTypeToString(subType);
        case 1:
            return RSNodeCommandTypeToString(subType);
        case 2:
            return RSCanvasNodeCommandTypeToString(subType);
        case 5:
            return RSRootNodeCommandTypeToString(subType);
        default:
            return std::to_string(subType);
    }
}

void RSTransactionData::DumpLog() const
{
    // Use this function as payload dumper for now
    RS_LOGD("RSTransactionData: payload size %{public}ld", payload_.size());
    for (size_t i = 0; i < payload_.size(); i++) {
        auto& command = std::get<2>(payload_[i]);
        auto rsNodeId = command->GetNodeId();
        auto cmdType = command->GetType();
        auto cmdSubType = command->GetSubType();

        RS_LOGD("RSTransactionData: command type %{public}s, subtype %{public}s, RSNode id %{public}d, frameNode id %{public}d, tag %{public}s",
            RSCommandTypeToString(cmdType), RSCommandSubTypeToString(cmdType, cmdSubType).c_str(),
            rsNodeId, GetFrameNodeId(rsNodeId), GetFrameNodeTag(rsNodeId).c_str());
    }
}

} // namespace Rosen
} // namespace Keels
